# Various hop calculations
module Hops

  # Calculates hop utilization.
  #
  # @note
  #     Extract must be measured in specific gravity
  #
  # @example
  #     Hops.utilization 20, 1.055
  #
  # @param [Fixnum] time remaining boil time
  # @param [Float] extract specific gravity of wort
  # @return [Float] hop utilization
  def self.utilization time, extract
    ((extract_adjustment extract) *
    ((time_adjustment time) / 4.15)).round 2
  end

  # Calculate boil time adjustment
  #
  # @example
  #    Hops.time_adjustment 60
  #
  # @param [Fixnum] time remaining boil time
  # @return [Float] boil time adjustment
  def self.time_adjustment time
    (1 - Math.exp(-0.04 * time)).round 2
  end

  # Calculate extract adjustment
  #
  # @note
  #     Extract must be measured in specific gravity
  #
  # @example
  #    Hops.extract_adjustment 1.065
  #
  # @param [Float] extract specific gravity of wort
  # @return [Float] extract adjustment
  def self.extract_adjustment extract
    (1.65 * 0.000125 ** (extract - 1)).round 2
  end

  # Calculates IBUs for hop addition
  #
  # @note
  #     Extract must be measured specific gravity, mass in grams and volume in liters
  #
  # @example
  #     Hops.ibus({
  #       alpha: 12.4,
  #       extract: 1.055,
  #       mass: 56,
  #       time: 90,
  #       volume: 20
  #     })
  #
  # @param [Hash] data data required to calculate IBUs; `alpha`, `extract`, `mass`, `time`, `volume`
  # @return [Fixnum] IBUs from hop addition
  def self.ibus data
    (extract_adjustment(data[:extract]) *
     time_adjustment(data[:time]) * (data[:alpha] / 100) *
     data[:mass] * 1000 / (data[:volume] * 4.15)).round
  end

  # Calculates hop mass required to achieve specific IBUs.
  #
  # @note
  #     Extract must be measured specific gravity, mass in grams and volume in liters
  #
  # @example
  #     Hops.hop_mass_required({
  #       alpha: 1.05,
  #       extract: 16,
  #       ibus: 25,
  #       time: 60,
  #       volume: 20
  #     })
  #
  # @param [Hash] data data required to calculate hop mass required; `alpha`, `extract`, `ibus`, `time`, `volume`
  # @return [Float] hop mass required to achieve specific IBUs
  def self.mass_required data
    ((data[:volume] * data[:ibus]) /
     (utilization(data[:time], data[:extract]) *
      data[:alpha] * 10)).round
  end

  # Calculate milliliters of HopShot required to achieve specific IBUs.
  #
  # @note
  #     Extract must be measured specific gravity, mass in grams and volume in liters
  #
  # @example
  #     Hops.hopshot_required({
  #       alpha: 13.5,
  #       extract: 1.05,
  #       ibus: 25,
  #       time: 60,
  #       volume: 20
  #     })
  #
  # @param [Hash] data data required to calculate HopShot required; `alpha`, `extract`, `ibus`, `time`, `volume`
  # @return [Float] milliliters of HopShot required to achieve specific IBUs
  def self.hopshot_required data
    unadjusted_amount = (data[:ibus] / 10.0) * (data[:volume] / 19.0)

    hopshot_required =  unadjusted_amount +
                       (unadjusted_amount * hopshot_extract_adjustment(data[:extract]))

    if data[:time] >= 90
      hopshot_required = hopshot_required - (unadjusted_amount * 0.1)
    end

    hopshot_required.round 1
  end

  # Calculate extract adjustment for HopShot
  #
  # @note
  #     Extract must be measured in specific gravity
  #
  # @example
  #    Hops.hopshot_extract_adjustment 1.065
  #
  # @param [Float] extract specific gravity of wort
  # @return [Float] HopShot extract adjustment
  def self.hopshot_extract_adjustment extract
    if extract >= 1.08 && extract < 1.1
      0.1
    elsif extract >= 1.1 && extract < 1.15
      0.2
    elsif extract >= 1.15
      0.3
    else
      0
    end
  end

end
